﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Data;
using System.ComponentModel;
using System.Reflection;
using System.IO;
using System.Runtime.Serialization.Formatters.Binary;

namespace MYTech.Data
{
    public class DataTableHelper
    {
        /// <summary>
        /// 将一组对象转化为DataTable,列名称为对象的各个属性
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="list">值</param>
        /// <returns></returns>
        public static DataTable ConvertTo<T>(IList<T> list) where T : class, new()
        {
            DataTable tbl = CreateTable<T>();
            Type entType = typeof(T);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
            foreach (T item in list)
            {
                DataRow row = tbl.NewRow();
                foreach (PropertyDescriptor prop in properties)
                {
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                }
                tbl.Rows.Add(row);
            }
            return tbl;
        }

        /// <summary>
        /// 将一组对象转化为DataTable,列名称为对象的各个属性
        /// </summary>
        /// <typeparam name="T">类型</typeparam>
        /// <param name="list">值</param>
        /// <returns></returns>
        public static DataTable ConvertTo<T>(params T[] list) where T : class, new()
        {
            DataTable tbl = CreateTable<T>();
            Type entType = typeof(T);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
            foreach (T item in list)
            {
                DataRow row = tbl.NewRow();
                foreach (PropertyDescriptor prop in properties)
                {
                    row[prop.Name] = prop.GetValue(item) ?? DBNull.Value;
                }
                tbl.Rows.Add(row);
            }
            return tbl;
        }

        /// <summary>
        /// 根据对象类型创建DataTable，列名称为对象的各个属性
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <returns></returns>
        public static DataTable CreateTable<T>() where T : class, new()
        {
            Type entType = typeof(T);
            DataTable tbl = new DataTable(entType.Name);
            PropertyDescriptorCollection properties = TypeDescriptor.GetProperties(entType);
            foreach (PropertyDescriptor prop in properties)
            {
                Type colType = prop.PropertyType;
                if ((colType.IsGenericType) && (colType.GetGenericTypeDefinition() == typeof(Nullable<>)))
                {
                    colType = colType.GetGenericArguments()[0];
                }
                tbl.Columns.Add(prop.Name, colType);
            }
            return tbl;
        }

        private static void SetList<T>(ref List<T> list, List<PropertyInfo> infos, DataTable dt, bool dateTimeToString) where T : class, new()
        {
            foreach (DataRow dr in dt.Rows)
            {
                T model = new T();
                infos.ForEach(p =>
                {
                    try
                    {
                        if (dr[p.Name] != DBNull.Value)//判断属性在不为空
                        {
                            object tempValue = dr[p.Name];
                            if (dr[p.Name].GetType() == typeof(DateTime) && dateTimeToString)//判断是否为时间
                                tempValue = dr[p.Name].ToString();

                            p.SetValue(model, tempValue, null);//设置

                        }
                    }
                    catch
                    {
                    }
                });
                list.Add(model);
            }
        }

        /// <summary>
        /// DataTable方法：将DataTable类型转化为指定类型的实体集合
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="dateTimeToString">是否需要将日期转换为字符串，默认为转换,值为true</param>
        /// <returns></returns>
        public static List<T> ToModelList<T>(DataTable dt, bool dateTimeToString) where T : class, new()
        {
            List<T> list = new List<T>();
            if (dt != null)
            {
                List<PropertyInfo> infos = new List<PropertyInfo>();
                Array.ForEach<PropertyInfo>(typeof(T).GetProperties(), p =>
                {
                    if (dt.Columns.Contains(p.Name))
                    {
                        infos.Add(p);
                    }
                });//获取类型的属性集合
                SetList<T>(ref list, infos, dt, dateTimeToString);
            }
            return list;
        }

        /// <summary>
        /// DataTable方法：将DataTable类型转化为指定类型的实体集合
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <returns></returns>
        public static List<T> ToModelList<T>(DataTable dt) where T : class, new()
        {
            return ToModelList<T>(dt, true);
        }

        /// <summary>
        /// DataRow方法：将DataRow类型转化为指定类型的实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <param name="dr"></param>
        /// <param name="dateTimeToString">是否需要将日期转换为字符串，默认为转换,值为true</param>
        /// <returns></returns>
        public static T ToModel<T>(DataRow dr, bool dateTimeToString) where T : class, new()
        {
            if (dr != null)
                return ToModelList<T>(dr.Table, dateTimeToString)[0];
            return null;
        }

        /// <summary>
        /// DataRow方法：将DataRow类型转化为指定类型的实体
        /// </summary>
        /// <typeparam name="T">实体类型</typeparam>
        /// <returns></returns>
        public static T ToModel<T>(DataRow dr) where T : class, new()
        {
            return ToModel<T>(dr, true);
        }

        /// <summary>
        /// 调整DataTable列顺序，当在columns数组中存在result中不包含的列，则在result中添加这些列；如果result中的部分列在columns中不存在，则先按columns列的顺序排序，然后将result中的其他列保持原序后置于columns的列后。
        /// [例如：result如：|COLUMN6|COLUMN5|COLUMN1|COLUMN2|，columns如：|COLUMN1|COLUMN2|COLUMN3|，最终结果列为：|COLUMN1|COLUMN2|COLUMN3|COLUMN6|COLUMN5|]
        /// </summary>
        /// <param name="result"></param>
        /// <param name="columns"></param>
        /// <returns></returns>
        public static DataTable OrdinalTable(DataTable result, DataColumn[] columns)
        {
            try
            {
                int maxColumn = 0;
                for (int i = 0; i < result.Columns.Count; i++)//TODO:找到result中的所有列中在columns中最后出现的列的序号,即在maxColumn之后的列在result中都不存在
                {
                    for (int j = 0; j < columns.Length; j++)
                    {
                        if (result.Columns[i].ColumnName.ToUpper() == columns[j].ColumnName.ToUpper())
                        {
                            if (maxColumn < j)
                            {
                                maxColumn = j;
                                break;
                            }
                        }
                    }
                }
                for (int j = 0; j < columns.Length; j++)
                {
                    if (j <= maxColumn && result.Columns.IndexOf(columns[j].ColumnName) < 0)//TODO:添加0~maxColumn中result不存在的列到result中
                    {
                        result.Columns.Add(columns[j].ColumnName.ToUpper(), columns[j].DataType);
                    }
                    else if (j > maxColumn)//TODO:添加maxColumn之后的列到result中
                    {
                        result.Columns.Add(columns[j].ColumnName.ToUpper(), columns[j].DataType);
                    }
                }
                for (int i = 0; i < columns.Length; i++)
                {
                    for (int j = 0; j < (columns.Length > result.Columns.Count ? columns.Length : result.Columns.Count); j++)//将result中存在但是columns中为给出的列排在最后
                    {
                        if (columns[i].ColumnName.ToUpper() == result.Columns[j].ColumnName.ToUpper())
                        {
                            result.Columns[j].SetOrdinal(i);
                            break;
                        }
                    }
                }
                return result;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// 调整DataTable列顺序，当在columns数组中存在result中不包含的列，则在result中添加这些列；如果result中的部分列在columns中不存在，则先按columns列的顺序排序，然后将result中的其他列保持原序后置于columns的列后。
        /// [例如：result如：|COLUMN6|COLUMN5|COLUMN1|COLUMN2|，columns如：|COLUMN1|COLUMN2|COLUMN3|，最终结果列为：|COLUMN1|COLUMN2|COLUMN3|COLUMN6|COLUMN5|]
        /// </summary>
        /// <param name="result"></param>
        /// <param name="columnNames"></param>
        /// <returns></returns>
        public static DataTable OrdinalTable(DataTable result, string[] columnNames)
        {
            DataColumn[] columns = new DataColumn[columnNames.Length];
            for (int i = 0; i < columnNames.Length; i++)
            {
                columns[i] = new DataColumn(columnNames[i]);
            }
            return OrdinalTable(result, columns);
        }

        /// <summary>
        /// 将DataTable转化为字节数组
        /// </summary>
        /// <param name="dt"></param>
        /// <returns></returns>
        public static byte[] ConvertToBytes(DataTable dt)
        {
            using (MemoryStream memory = new MemoryStream())
            {
                BinaryFormatter b = new BinaryFormatter();
                b.Serialize(memory, dt);
                byte[] buff = memory.GetBuffer();
                memory.Close();
                return buff;
            }
        }

        /// <summary>
        /// 将字节数字转化为对应的DataTable
        /// </summary>
        /// <param name="buff"></param>
        /// <returns></returns>
        public static DataTable ConvertToDataTable(byte[] buff)
        {
            using (MemoryStream memory = new MemoryStream(buff))
            {
                BinaryFormatter b = new BinaryFormatter();
                DataTable result = (DataTable)b.Deserialize(memory);
                memory.Close();
                return result;
            }
        }
    }
}
